{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Guaranteeing valid output syntax\n",
    "\n",
    "Large language models are great at generating useful outputs, but they are not great at guaranteeing that those outputs follow a specific format. This can cause problems when we want to use the outputs of a language model as input to another system. For example, if we want to use a language model to generate a JSON object, we need to make sure that the output is valid JSON. This can be a real pain with standard APIs, but with `guidance` we can both accelerate inference speed and ensure that generated JSON is always valid.\n",
    "\n",
    "This notebook shows how to generate a JSON object we know will have a valid format. The example used here is a generating a random character profile for a game, but the ideas are readily applicable to any scneeario where you want JSON output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f300ae3e846b4f08a1f53a1881cdfc12",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Loading checkpoint shards:   0%|          | 0/2 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import guidance\n",
    "\n",
    "# we use LLaMA here, but any GPT-style model will do\n",
    "guidance.llm = guidance.llms.Transformers(\"huggyllama/llama-7b\", device=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id=\"guidance-stop-button-f299b401-e280-4caf-b30c-48565b695e45\" style=\"cursor: pointer; margin: 0px; display: none; float: right; padding: 3px; border-radius: 4px 4px 4px 4px; border: 0px solid rgba(127, 127, 127, 1); padding-left: 10px; padding-right: 10px; font-size: 13px; background-color: rgba(127, 127, 127, 0.25);\">Stop program</div><div id=\"guidance-content-f299b401-e280-4caf-b30c-48565b695e45\"><pre style='margin: 0px; padding: 0px; padding-left: 8px; margin-left: -8px; border-radius: 0px; border-left: 1px solid rgba(127, 127, 127, 0.2); white-space: pre-wrap; font-family: ColfaxAI, Arial; font-size: 15px; line-height: 23px;'>The following is a character profile for an RPG game in JSON format.\n",
       "```json\n",
       "{\n",
       "    &quot;description&quot;: &quot;<span style='background-color: rgba(0, 138.56128016, 250.76166089, 0.25); display: inline;' title='{{description}}'>A quick and nimble fighter.</span>&quot;,\n",
       "    &quot;name&quot;: &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;name&#x27;}}'>Ranger</span>&quot;,\n",
       "    &quot;age&quot;: <span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;age&#x27; pattern=&#x27;[0-9]+&#x27; stop=&#x27;,&#x27;}}'>20</span>,\n",
       "    &quot;armor&quot;: &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{#select &#x27;armor&#x27;}}leather{{or}}chainmail{{or}}plate{{/select}}'>plate</span>&quot;,\n",
       "    &quot;weapon&quot;: &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{select &#x27;weapon&#x27; options=valid_weapons}}'>sword</span>&quot;,\n",
       "    &quot;class&quot;: &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;class&#x27;}}'>fighter</span>&quot;,\n",
       "    &quot;mantra&quot;: &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;mantra&#x27;}}'>I am the ranger.</span>&quot;,\n",
       "    &quot;strength&quot;: <span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;strength&#x27; pattern=&#x27;[0-9]+&#x27; stop=&#x27;,&#x27;}}'>10</span>,\n",
       "    &quot;items&quot;: [<span style='opacity: 1.0; display: inline; background-color: rgba(165, 165, 165, 0.1);' title='{{#geneach &#x27;items&#x27; num_iterations=3}}\n",
       "        &quot;{{gen &#x27;this&#x27;}}&quot;,{{/geneach}}'>\n",
       "        &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;this&#x27;}}'>dagger</span>&quot;,\n",
       "        &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;this&#x27;}}'>shield</span>&quot;,\n",
       "        &quot;<span style='background-color: rgba(0, 165, 0, 0.25); opacity: 1.0; display: inline;' title='{{gen &#x27;this&#x27;}}'>bow</span>&quot;,</span>\n",
       "    ]\n",
       "}```</pre></div>\n",
       "<script type=\"text/javascript\">(()=>{var t={296:(t,e,n)=>{var i=NaN,o=\"[object Symbol]\",r=/^\\s+|\\s+$/g,a=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,d=parseInt,u=\"object\"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,l=\"object\"==typeof self&&self&&self.Object===Object&&self,f=u||l||Function(\"return this\")(),h=Object.prototype.toString,p=Math.max,m=Math.min,g=function(){return f.Date.now()};function b(t){var e=typeof t;return!!t&&(\"object\"==e||\"function\"==e)}function y(t){if(\"number\"==typeof t)return t;if(function(t){return\"symbol\"==typeof t||function(t){return!!t&&\"object\"==typeof t}(t)&&h.call(t)==o}(t))return i;if(b(t)){var e=\"function\"==typeof t.valueOf?t.valueOf():t;t=b(e)?e+\"\":e}if(\"string\"!=typeof t)return 0===t?t:+t;t=t.replace(r,\"\");var n=s.test(t);return n||c.test(t)?d(t.slice(2),n?2:8):a.test(t)?i:+t}t.exports=function(t,e,n){var i,o,r,a,s,c,d=0,u=!1,l=!1,f=!0;if(\"function\"!=typeof t)throw new TypeError(\"Expected a function\");function h(e){var n=i,r=o;return i=o=void 0,d=e,a=t.apply(r,n)}function v(t){var n=t-c;return void 0===c||n>=e||n<0||l&&t-d>=r}function _(){var t=g();if(v(t))return w(t);s=setTimeout(_,function(t){var n=e-(t-c);return l?m(n,r-(t-d)):n}(t))}function w(t){return s=void 0,f&&i?h(t):(i=o=void 0,a)}function j(){var t=g(),n=v(t);if(i=arguments,o=this,c=t,n){if(void 0===s)return function(t){return d=t,s=setTimeout(_,e),u?h(t):a}(c);if(l)return s=setTimeout(_,e),h(c)}return void 0===s&&(s=setTimeout(_,e)),a}return e=y(e)||0,b(n)&&(u=!!n.leading,r=(l=\"maxWait\"in n)?p(y(n.maxWait)||0,e):r,f=\"trailing\"in n?!!n.trailing:f),j.cancel=function(){void 0!==s&&clearTimeout(s),d=0,i=c=o=s=void 0},j.flush=function(){return void 0===s?a:w(g())},j}},777:t=>{var e,n,i=Math.max,o=(e=function(t,e){return function(t,e,n){if(\"function\"!=typeof t)throw new TypeError(\"Expected a function\");return setTimeout((function(){t.apply(void 0,n)}),1)}(t,0,e)},n=i(void 0===n?e.length-1:n,0),function(){for(var t=arguments,o=-1,r=i(t.length-n,0),a=Array(r);++o<r;)a[o]=t[n+o];o=-1;for(var s=Array(n+1);++o<n;)s[o]=t[o];return s[n]=a,function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}(e,this,s)});t.exports=o}},e={};function n(i){var o=e[i];if(void 0!==o)return o.exports;var r=e[i]={exports:{}};return t[i](r,r.exports,n),r.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var i in e)n.o(e,i)&&!n.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},n.g=function(){if(\"object\"==typeof globalThis)return globalThis;try{return this||new Function(\"return this\")()}catch(t){if(\"object\"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{\"use strict\";const t=t=>{const e=new Set;do{for(const n of Reflect.ownKeys(t))e.add([t,n])}while((t=Reflect.getPrototypeOf(t))&&t!==Object.prototype);return e};function e(e,{include:n,exclude:i}={}){const o=t=>{const e=e=>\"string\"==typeof e?t===e:e.test(t);return n?n.some(e):!i||!i.some(e)};for(const[n,i]of t(e.constructor.prototype)){if(\"constructor\"===i||!o(i))continue;const t=Reflect.getOwnPropertyDescriptor(n,i);t&&\"function\"==typeof t.value&&(e[i]=e[i].bind(e))}return e}var i=n(777),o=n.n(i),r=n(296),a=n.n(r);class s{constructor(t,n){e(this),this.interfaceId=t,this.callbackMap={},this.data={},this.pendingData={},this.jcomm=new c(\"guidance_interface_target_\"+this.interfaceId,this.updateData,\"open\"),this.debouncedSendPendingData500=a()(this.sendPendingData,500),this.debouncedSendPendingData1000=a()(this.sendPendingData,1e3),n&&o()(n)}send(t,e){this.addPendingData(t,e),this.sendPendingData()}sendEvent(t){for(const e of Object.keys(t))this.addPendingData(e,t[e]);this.sendPendingData()}debouncedSendEvent500(t){for(const e of Object.keys(t))this.addPendingData(e,t[e]);this.debouncedSendPendingData500()}debouncedSend500(t,e){this.addPendingData(t,e),this.debouncedSendPendingData500()}debouncedSend1000(t,e){this.addPendingData(t,e),this.debouncedSendPendingData1000()}addPendingData(t,e){Array.isArray(t)||(t=[t]);for(const n in t)this.pendingData[t[n]]=e}updateData(t){t=JSON.parse(t.data);for(const e in t)this.data[e]=t[e];for(const e in t)e in this.callbackMap&&this.callbackMap[e](this.data[e])}subscribe(t,e){this.callbackMap[t]=e,o()((e=>this.callbackMap[t](this.data[t])))}sendPendingData(){this.jcomm.send_data(this.pendingData),this.pendingData={}}}class c{constructor(t,e,n=\"open\"){this._fire_callback=this._fire_callback.bind(this),this._register=this._register.bind(this),this.jcomm=void 0,this.callback=e,void 0!==window.Jupyter?\"register\"===n?Jupyter.notebook.kernel.comm_manager.register_target(t,this._register):(this.jcomm=Jupyter.notebook.kernel.comm_manager.new_comm(t),this.jcomm.on_msg(this._fire_callback)):void 0!==window._mgr&&(\"register\"===n?window._mgr.widgetManager.proxyKernel.registerCommTarget(t,this._register):(this.jcomm=window._mgr.widgetManager.proxyKernel.createComm(t),this.jcomm.open({},\"\"),this.jcomm.onMsg=this._fire_callback))}send_data(t){void 0!==this.jcomm?this.jcomm.send(t):console.error(\"Jupyter comm module not yet loaded! So we can't send the message.\")}_register(t,e){this.jcomm=t,this.jcomm.on_msg(this._fire_callback)}_fire_callback(t){this.callback(t.content.data)}}class d{constructor(t,n){e(this),this.id=t,this.comm=new s(t),this.comm.subscribe(\"append\",this.appendData),this.comm.subscribe(\"replace\",this.replaceData),this.comm.subscribe(\"event\",this.eventOccurred),this.element=document.getElementById(\"guidance-content-\"+t),this.stop_button=document.getElementById(\"guidance-stop-button-\"+t),this.stop_button.onclick=()=>this.comm.send(\"event\",\"stop\")}appendData(t){t&&(this.stop_button.style.display=\"inline-block\",this.element.innerHTML+=t)}replaceData(t){t&&(this.stop_button.style.display=\"inline-block\",this.element.innerHTML=t)}eventOccurred(t){\"complete\"===t&&(this.stop_button.style.display=\"none\")}}window._guidanceDisplay=function(t,e){return new d(t,e)}})()})();; window._guidanceDisplay(\"f299b401-e280-4caf-b30c-48565b695e45\");</script>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# we can pre-define valid option sets\n",
    "valid_weapons = [\"sword\", \"axe\", \"mace\", \"spear\", \"bow\", \"crossbow\"]\n",
    "\n",
    "# define the prompt\n",
    "program = guidance(\"\"\"The following is a character profile for an RPG game in JSON format.\n",
    "```json\n",
    "{\n",
    "    \"description\": \"{{description}}\",\n",
    "    \"name\": \"{{gen 'name'}}\",\n",
    "    \"age\": {{gen 'age' pattern='[0-9]+' stop=','}},\n",
    "    \"armor\": \"{{#select 'armor'}}leather{{or}}chainmail{{or}}plate{{/select}}\",\n",
    "    \"weapon\": \"{{select 'weapon' options=valid_weapons}}\",\n",
    "    \"class\": \"{{gen 'class'}}\",\n",
    "    \"mantra\": \"{{gen 'mantra'}}\",\n",
    "    \"strength\": {{gen 'strength' pattern='[0-9]+' stop=','}},\n",
    "    \"items\": [{{#geneach 'items' num_iterations=3}}\n",
    "        \"{{gen 'this'}}\",{{/geneach}}\n",
    "    ]\n",
    "}```\"\"\")\n",
    "\n",
    "# execute the prompt\n",
    "out = program(description=\"A quick and nimble fighter.\", valid_weapons=valid_weapons)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "{\n",
      "    \"description\": \"A quick and nimble fighter.\",\n",
      "    \"name\": \"Ranger\",\n",
      "    \"age\": 20,\n",
      "    \"armor\": \"plate\",\n",
      "    \"weapon\": \"sword\",\n",
      "    \"class\": \"fighter\",\n",
      "    \"mantra\": \"I am the ranger.\",\n",
      "    \"strength\": 10,\n",
      "    \"items\": [\n",
      "        \"dagger\",\n",
      "        \"shield\",\n",
      "        \"bow\",\n",
      "    ]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "# we have now generated a valid json string that is mixure of generated and inserted text\n",
    "print(str(out).split(\"```json\")[1][:-3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'llm': <guidance.llms._transformers.Transformers at 0x7fae471f63e0>,\n",
       " 'description': 'A quick and nimble fighter.',\n",
       " 'valid_weapons': ['sword', 'axe', 'mace', 'spear', 'bow', 'crossbow'],\n",
       " 'name': 'Ranger',\n",
       " 'age': '20',\n",
       " 'armor': 'plate',\n",
       " 'weapon': 'sword',\n",
       " 'class': 'fighter',\n",
       " 'mantra': 'I am the ranger.',\n",
       " 'strength': '10',\n",
       " 'items': ['dagger', 'shield', 'bow']}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# and we also have a valid python dictionary\n",
    "out.variables()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{\"description\": \"A quick and nimble fighter.\", \"valid_weapons\": [\"sword\", \"axe\", \"mace\", \"spear\", \"bow\", \"crossbow\"], \"name\": \"Ranger\", \"age\": \"20\", \"armor\": \"plate\", \"weapon\": \"sword\", \"class\": \"fighter\", \"mantra\": \"I am the ranger.\", \"strength\": \"10\", \"items\": [\"dagger\", \"shield\", \"bow\"]}'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ...that we could also serialize the dictionary to JSON\n",
    "import json\n",
    "json.dumps({k:v for k,v in out.variables().items() if k != \"llm\"})"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<hr style=\"height: 1px; opacity: 0.5; border: none; background: #cccccc;\">\n",
    "<div style=\"text-align: center; opacity: 0.5\">Have an idea for more helpful examples? Pull requests that add to this documentation notebook are encouraged!</div>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
